package mailx

import (
	"bytes"
	"fmt"
	"io"
	"mime"
	"net/textproto"
	"path/filepath"
	"strings"
)

var canonicalKey = textproto.CanonicalMIMEHeaderKey

//goland:noinspection GoUnusedConst,GoSnakeCaseUsage
const (
	HeaderContentTransferEncoding = "Content-Transfer-Encoding"
	HeaderContentType             = "Content-Type"
	HeaderContentDisposition      = "Content-Disposition"

	CTE_Q = "quoted-printable"
	CTE_B = "base64"
)

// Header 媒体头
type Header struct {
	entries  []*headerEntry
	boundary string
}

type headerEntry struct {
	k, v string
}

func (hdr *Header) add(k, v string) {
	if hdr != nil && v != "" {
		hdr.entries = append(hdr.entries, &headerEntry{k: k, v: v})
	}
}

func (hdr *Header) del(k string) {
	if hdr != nil {
		hdr.entries = sliceMap(hdr.entries, func(e *headerEntry) (*headerEntry, bool) { return e, e.k != k })
	}
}

// Add 添加键值对
func (hdr *Header) Add(k, v string) {
	hdr.add(canonicalKey(k), v)
}

// Del 删除指定键
func (hdr *Header) Del(k string) {
	hdr.del(canonicalKey(k))
}

// Set 设置指定键的值，存在则替换，不存在则添加，值为空则删除
func (hdr *Header) Set(k, v string) {
	k = canonicalKey(k)
	hdr.del(k)
	hdr.add(k, v)
}

// SetText 设置指定键的值，值会被编码
func (hdr *Header) SetText(k, v string) {
	hdr.Set(k, quoted(v))
}

// Get 获取值
func (hdr *Header) Get(k string) (v string) {
	if hdr != nil {
		k = canonicalKey(k)
		if e, ok := sliceFind(hdr.entries, func(e *headerEntry) bool { return e.k == k }); ok {
			return e.v
		}
	}
	return
}

// HeaderBytes 编码成字节
func (hdr *Header) HeaderBytes() []byte {
	if hdr != nil {
		var b bytes.Buffer
		for _, e := range hdr.entries {
			_, _ = fmt.Fprintf(&b, "%s: %s\r\n", e.k, e.v)
		}
		_, _ = fmt.Fprintf(&b, "\r\n")
		return b.Bytes()
	}
	return nil
}

func (hdr *Header) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write(hdr.HeaderBytes())
	return int64(n), err
}

func (hdr *Header) Each(walkFn func(k, v string)) {
	if hdr != nil {
		for _, e := range hdr.entries {
			walkFn(e.k, e.v)
		}
	}
}

/***extend***/

// SetContentType 设置ContentType
func (hdr *Header) SetContentType(mimetype string, params ...string) {
	pm := makeParams(params...)
	switch {
	case strings.HasPrefix(mimetype, "multipart/"):
		hdr.boundary = generateBoundary(strings.TrimPrefix(mimetype, "multipart/"))
		pm["boundary"] = hdr.boundary
	case strings.HasPrefix(mimetype, "text/"):
		pm["charset"] = codepage
	}
	hdr.Set(HeaderContentType, mime.FormatMediaType(mimetype, pm))
}

// SetMultipartType 设置为多媒体类型
func (hdr *Header) SetMultipartType(partType string) {
	hdr.SetContentType("multipart/" + partType)
}

// SetTextType 设置ContentType为文本类型
func (hdr *Header) SetTextType(textType string) {
	hdr.SetContentType("text/" + textType)
}

// SetContentDisposition 设置内容的编码处理方式
func (hdr *Header) SetContentDisposition(contentDisposition string, params ...string) {
	hdr.Set(HeaderContentDisposition, mime.FormatMediaType(contentDisposition, makeParams(params...)))
}

// SetContentTransferEncoding 设置内容的传输编码方式，name: "quoted-printable", "base64"
func (hdr *Header) SetContentTransferEncoding(cte string) {
	hdr.Set(HeaderContentTransferEncoding, cte)
}

// GetContentTransferEncoding 获取 ContentTransferEncoding
func (hdr *Header) GetContentTransferEncoding() string {
	return hdr.Get(HeaderContentTransferEncoding)
}

// Boundary is empty but content-type is multipart
func (hdr *Header) Boundary() string {
	return hdr.boundary
}

/***extend***/

// AttachmentHeader 设置附件头部
func createAttachmentHeader(disp, name string) *Header {
	hdr := &Header{}
	mimetype := mime.TypeByExtension(filepath.Ext(name))
	if mimetype == "" {
		mimetype = "application/octet-stream"
	}
	if disp == "inline" {
		hdr.SetContentType(mimetype)
		hdr.SetContentDisposition(disp)
		hdr.Set("content-id", "<"+quoted(name)+">")
	} else {
		hdr.SetContentType(mimetype, "name", quoted(name))
		hdr.SetContentDisposition(disp, "filename", quoted(name))
	}
	hdr.SetContentTransferEncoding("base64")

	return hdr
}

func createTextHeader(textType string) *Header {
	hdr := &Header{}
	hdr.SetTextType(textType)
	hdr.SetContentTransferEncoding("base64")
	return hdr
}
